#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_gfxPrimitives.h"
#include <string>
#include <sstream>
#include <iostream>
#include "MyTimer.h"
#include <math.h>
#include <vector>

#define SCREEN_WIDTH						1040
#define SCREEN_HEIGHT						740
#define SCREEN_BPP							32

#define AVATAR_WIDTH						20
#define AVATAR_HEIGHT						20

#define FRAMES_PER_SECOND					20

#define MUL8_UP								0
#define MUL8_RIGHT							1
#define MUL8_DOWN							2
#define MUL8_LEFT							3
#define MUL8_UP_LEFT						4
#define MUL8_DOWN_RIGHT						5
#define MUL8_UP_RIGHT						6
#define MUL8_DOWN_LEFT						7

#define VELOCITY							3

#define HALF_FIELD_VERTICALLY				370
#define	HALF_FIELD_HORIZONTALLY				520

// Define surfaces
SDL_Surface* screen = NULL;
SDL_Surface* background = NULL;
SDL_Surface* message = NULL;

TTF_Font* font = NULL;
SDL_Color textColor = {255, 255, 255};
SDL_Event event;
MyTimer fps;

// Rectangle for MU-L8 images
SDL_Rect mul8[8];

class Ball {
protected:
	SDL_Rect position;
	int radius;
	int xVel;
	int yVel;
	SDL_Surface* sprite;
public:
	Ball();
	virtual ~Ball();
	void show();
	std::vector<SDL_Rect> getBall();
	SDL_Rect getPosition();
};

class StationaryBall : public Ball {
public:
	void move(SDL_Rect);
};

class MoveableBall : public Ball {
public:
	MoveableBall();
	virtual ~MoveableBall();
	void move();
	void handle_input();
};

class Avatar {
private:
	SDL_Rect position;
	int currentDirection;
	int xVel, yVel;
	SDL_Surface* avatar;
public:
	Avatar();
	virtual ~Avatar();
	void move();
	void autonomousMove(SDL_Rect);
	void handle_input();
	void show();
};

bool init() {
	if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
		return false;
	}

	// Set up the screen
	screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
			SDL_SWSURFACE | SDL_FULLSCREEN | SDL_DOUBLEBUF);

	// If there was an error in setting up the screen
	if (screen == NULL) {
		return false;
	}

	if (TTF_Init() == -1) {
		return false;
	}

	// Set the window caption
	SDL_WM_SetCaption("SiMU-L8 Soccer Demo", NULL);

	mul8[0].x = 0;
	mul8[0].y = 0;
	mul8[0].w = 20;
	mul8[0].h = 20;

	mul8[1].x = 0;
	mul8[1].y = 26;
	mul8[1].w = 20;
	mul8[1].h = 19;

	mul8[2].x = 0;
	mul8[2].y = 51;
	mul8[2].w = 20;
	mul8[2].h = 19;

	mul8[3].x = 0;
	mul8[3].y = 76;
	mul8[3].w = 20;
	mul8[3].h = 19;

	mul8[4].x = 0;
	mul8[4].y = 101;
	mul8[4].w = 20;
	mul8[4].h = 19;

	mul8[5].x = 0;
	mul8[5].y = 126;
	mul8[5].w = 20;
	mul8[5].h = 19;

	mul8[6].x = 0;
	mul8[6].y = 151;
	mul8[6].w = 20;
	mul8[6].h = 19;

	mul8[7].x = 0;
	mul8[7].y = 176;
	mul8[7].w = 20;
	mul8[7].h = 19;

	return true;
}

void setup() {
	SDL_Surface* tempBackground = IMG_Load("src/soccer_field_updated_5_20.bmp");

	background = SDL_DisplayFormat(tempBackground);

	SDL_FreeSurface(tempBackground);

	font = TTF_OpenFont("src/DarkistheNight.ttf", 28);

	message = TTF_RenderText_Solid(font, "Collision", textColor);
}

void close() {
	//Free the loaded image
	SDL_FreeSurface( background );

	TTF_Quit();

	//Quit SDL
	SDL_Quit();
}

void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL) {
	// Make temp rectangle to hold offsets
	SDL_Rect offset;

	offset.x = x;
	offset.y = y;

	SDL_BlitSurface(source, clip, destination, &offset);
}

bool checkCollision(std::vector<SDL_Rect> a, std::vector<SDL_Rect> b) {
	int leftA, leftB, rightA, rightB, topA, topB, botA, botB;
	bool collision = false;
	for (unsigned i = 0; i < a.size(); i++) {
		leftA = a[i].x;
		rightA = a[i].x + a[i].w + 1;
		topA = a[i].y;
		botA = a[i].y + a[i].h + 1;

		for (unsigned j = 0; j < b.size(); j++) {
			leftB = b[i].x;
			rightB = b[i].x + b[i].w + 1;
			topB = b[i].y;
			botB = b[i].y + b[i].h + 1;

			if( ( ( botA <= topB ) || ( topA >= botB ) || ( rightA <= leftB ) || ( leftA >= rightB ) ) == false ) {
				//A collision is detected
				collision = true;
			}

			//			if (((topA <= botB && topA >= topB) || (botA <= botB && botA >= topB)) && ((leftA >= leftB && leftA <= leftB) || (rightA >= leftB && rightA <= rightB))) {
			//				collision = true;
			//			}
		}
	}

	return collision;
}

int main(int argc, char **argv) {

	init();

	// Create Avatar
	Avatar myAvatar;

	// Create Ball
	MoveableBall myBall;
	StationaryBall stationaryBall;

	setup();

	apply_surface(0, 0, background, screen);
	myBall.show();
	stationaryBall.show();

	if (SDL_Flip(screen) == -1) {
		return 1;
	}

	SDL_Rect myBallPosition = myBall.getPosition();
	bool quit = false;
	bool autonomous = false;

	while (!quit) {
		fps.start();
		while(SDL_PollEvent(&event)) {
			if (event.type == SDL_KEYDOWN) {
				if (event.key.keysym.sym == SDLK_q) {
					quit = true;
				}
				else if (event.key.keysym.sym == SDLK_RETURN) {
					autonomous = !(autonomous);
				}
			}
			// Check to see if user Xed out window
			else if (event.type == SDL_QUIT) {
				quit = true;
			}
		}

		if (autonomous) {
			myAvatar.autonomousMove(myBall.getPosition());
		}
		else {
			myAvatar.handle_input();
			myAvatar.move();
		}
		apply_surface(0, 0, background, screen);

		myAvatar.show();

		myBall.handle_input();
		myBall.move();
		myBall.show();
//		if (follow) {
//			stationaryBall.move(myBall.getPosition());
//		}
//		stationaryBall.show();

//		if (checkCollision(myBall.getBall(), stationaryBall.getBall())) {
//			apply_surface(0, 0, message, screen);
//		}

		myBallPosition = myBall.getPosition();

		lineRGBA(screen, 70, 370,
				myBallPosition.x + myBallPosition.w/2, myBallPosition.y + myBallPosition.h/2,
				0, 255, 255, 255);

		if (SDL_Flip(screen) == -1) {
			return 1;
		}

		if (fps.get_ticks() < 1000 / FRAMES_PER_SECOND) {
			SDL_Delay((1000 / FRAMES_PER_SECOND) - fps.get_ticks());
		}

	}

	close();

}

Avatar::Avatar() {
	// Initialize the offsets
	position.x = 0;
	position.y = 0;
	position.w = AVATAR_WIDTH;
	position.h = AVATAR_HEIGHT;

	// Initialize the velocities
	xVel = 0;
	yVel = 0;

	// Initialize the currentDirection
	currentDirection = MUL8_DOWN;

	SDL_Surface* tempAvatar = IMG_Load("src/MU-L8_standin.bmp");

	if (tempAvatar == NULL) {
		std::cout << "Could not find avatar." << std::endl;
	}
	else {
		avatar = SDL_DisplayFormat(tempAvatar);
		// Map the color key
		Uint32 colorkey = SDL_MapRGB(avatar->format, 0xFF, 0xFF, 0xFF);

		// Set all pixels of color R 0xFF, G 0xFF, B 0xFF to be transparent
		SDL_SetColorKey(avatar, SDL_SRCCOLORKEY, colorkey);
	}

	SDL_FreeSurface(tempAvatar);
}

Avatar::~Avatar() {
	SDL_FreeSurface(avatar);
}

void Avatar::autonomousMove(SDL_Rect goal) {
	if (goal.y < HALF_FIELD_VERTICALLY) {
		// Ball is on upper half of the field
		goal.y -= goal.h;
	}
	goal.x += goal.w;


	if (goal.x < position.x) {
		if (goal.y < (position.y)) {
			position.x -= VELOCITY / sqrt(2);
			position.y -= VELOCITY / sqrt(2);
			currentDirection = MUL8_UP_LEFT;
		}
		else if (goal.y > (position.y)) {
			position.x -= VELOCITY / sqrt(2);
			position.y += VELOCITY / sqrt(2);
			currentDirection = MUL8_DOWN_LEFT;
		}
		else {
			position.x -= VELOCITY;
			currentDirection = MUL8_LEFT;
		}
	}
	else if (goal.x > position.x) {
		if (goal.y < (position.y)) {
			position.x += VELOCITY / sqrt(2);
			position.y -= VELOCITY / sqrt(2);
			currentDirection = MUL8_UP_RIGHT;
		}
		else if (goal.y > (position.y)) {
			position.x += VELOCITY / sqrt(2);
			position.y += VELOCITY / sqrt(2);
			currentDirection = MUL8_DOWN_RIGHT;
		}
		else {
			position.x += VELOCITY;
			currentDirection = MUL8_RIGHT;
		}
	}
	else if (goal.y < (position.y)) {
		position.y -= VELOCITY;
		currentDirection = MUL8_UP;
	}
	else if (goal.y > (position.y)) {
		position.y += VELOCITY;
		currentDirection = MUL8_DOWN;
	}

}

void Avatar::handle_input() {
	Uint8* keystates = SDL_GetKeyState(NULL);
	if (keystates[SDLK_w]) {
		if (keystates[SDLK_a]) {
			xVel = (int) ((VELOCITY / sqrt(2)) * -1);
			yVel = (int) ((VELOCITY / sqrt(2)) * -1);
			currentDirection = MUL8_UP_LEFT;
		}
		else if (keystates[SDLK_d]) {
			xVel = (int) (VELOCITY / sqrt(2));
			yVel = (int) ((VELOCITY / sqrt(2)) * -1);
			currentDirection = MUL8_UP_RIGHT;
		}
		else {
			yVel = VELOCITY * -1;
			xVel = 0;
			currentDirection = MUL8_UP;
		}
	}
	else if (keystates[SDLK_s]) {
		if (keystates[SDLK_a]) {
			xVel = (int) ((VELOCITY / sqrt(2)) * -1);
			yVel = (int) (VELOCITY / sqrt(2));
			currentDirection = MUL8_DOWN_LEFT;
		}
		else if (keystates[SDLK_d]) {
			xVel = (int) (VELOCITY / sqrt(2));
			yVel = (int) (VELOCITY / sqrt(2));
			currentDirection = MUL8_DOWN_RIGHT;
		}
		else {
			yVel = VELOCITY;
			xVel = 0;
			currentDirection = MUL8_DOWN;
		}
	}
	else if (keystates[SDLK_a]) {
		xVel = VELOCITY * -1;
		yVel = 0;
		currentDirection = MUL8_LEFT;
	}
	else if (keystates[SDLK_d]) {
		xVel = VELOCITY;
		yVel = 0;
		currentDirection = MUL8_RIGHT;
	}
	else {
		xVel = 0;
		yVel = 0;
	}
}

void Avatar::move() {
	position.x += xVel;
	if (position.x < 0) {
		position.x = 0;
	}
	else if (position.x > SCREEN_WIDTH - AVATAR_WIDTH) {
		position.x = SCREEN_WIDTH - AVATAR_WIDTH;
	}

	position.y += yVel;
	if (position.y < 0) {
		position.y = 0;
	}
	else if (position.y > SCREEN_HEIGHT - AVATAR_HEIGHT) {
		position.y = SCREEN_HEIGHT - AVATAR_HEIGHT;
	}
}

void Avatar::show() {
	apply_surface(position.x, position.y, avatar, screen, &mul8[currentDirection]);
}

Ball::Ball() {
	position.x = 515;
	position.y = 365;
	xVel = 0;
	yVel = 0;
	radius = 5;
	position.w = 2*radius;
	position.h = 2*radius;
	sprite = NULL;
	SDL_Surface* tempAvatar = IMG_Load("src/ball.bmp");

	if (tempAvatar == NULL) {
		std::cout << "Could not find ball." << std::endl;
	}
	else {
		sprite = SDL_DisplayFormat(tempAvatar);
		// Map the color key
		Uint32 colorkey = SDL_MapRGB(sprite->format, 0xFF, 0xFF, 0xFF);

		// Set all pixels of color R 0xFF, G 0xFF, B 0xFF to be transparent
		SDL_SetColorKey(sprite, SDL_SRCCOLORKEY, colorkey);
	}

	SDL_FreeSurface(tempAvatar);
}

Ball::~Ball() {
	SDL_FreeSurface(sprite);
}



void Ball::show() {
	apply_surface(position.x, position.y, sprite, screen);
}

std::vector<SDL_Rect> Ball::getBall() {
	std::vector<SDL_Rect> result;
	result.resize(5);
	result[0].x = position.x + 2;
	result[0].y = position.y;
	result[0].w = 6;
	result[0].h = 1;

	result[1].x = position.x + 1;
	result[1].y = position.y + 1;
	result[1].w = 8;
	result[1].h = 1;

	result[2].x = position.x;
	result[2].y = position.y + 2;
	result[2].w = 10;
	result[2].h = 6;

	result[3].x = position.x + 1;
	result[3].y = position.y + 8;
	result[3].w = 8;
	result[3].h = 1;

	result[4].x = position.x + 2;
	result[4].y = position.y + 9;
	result[4].w = 6;
	result[4].h = 1;

	return result;
}

SDL_Rect Ball::getPosition() {
	return position;
}

/*
 * @override the default constructor
 */
MoveableBall::MoveableBall() {
	position.x = 515;
	position.y = 365;
	xVel = 0;
	yVel = 0;
	radius = 5;
	position.w = 2*radius;
	position.h = 2*radius;
	sprite = NULL;
	SDL_Surface* tempAvatar = IMG_Load("src/avatar.bmp");

	if (tempAvatar == NULL) {
		std::cout << "Could not find ball." << std::endl;
	}
	else {
		sprite = SDL_DisplayFormat(tempAvatar);
		// Map the color key
		Uint32 colorkey = SDL_MapRGB(sprite->format, 0xFF, 0xFF, 0xFF);

		// Set all pixels of color R 0xFF, G 0xFF, B 0xFF to be transparent
		SDL_SetColorKey(sprite, SDL_SRCCOLORKEY, colorkey);
	}

	SDL_FreeSurface(tempAvatar);
}

MoveableBall::~MoveableBall() {
	SDL_FreeSurface(sprite);
}

void MoveableBall::move() {
	position.x += xVel;
	if (position.x < 0) {
		position.x = 0;
	}
	else if (position.x > SCREEN_WIDTH - 2*radius) {
		position.x = SCREEN_WIDTH - 2*radius;
	}

	position.y += yVel;
	if (position.y < 0) {
		position.y = 0;
	}
	else if (position.y > SCREEN_HEIGHT - 2*radius) {
		position.y = SCREEN_HEIGHT - 2*radius;
	}
}

void MoveableBall::handle_input() {
	Uint8* keystates = SDL_GetKeyState(NULL);
	if (keystates[SDLK_UP]) {
		yVel = VELOCITY * -1;
	}
	else if (keystates[SDLK_DOWN]) {
		yVel = VELOCITY;
	}
	else {
		yVel = 0;
	}

	if (keystates[SDLK_LEFT]) {
		xVel = VELOCITY * -1;
	}
	else if (keystates[SDLK_RIGHT]) {
		xVel = VELOCITY;
	}
	else {
		xVel = 0;
	}

}

void StationaryBall::move(SDL_Rect goal) {
	if (goal.x < position.x) {
		if (goal.y < position.y) {
			position.x -= 1;
			position.y -= 1;
		}
		else if (goal.y > position.y) {
			position.x -= 1;
			position.y += 1;
		}
		else {
			position.x -= 2;
		}
	}
	else if (goal.x > position.x) {
		if (goal.y < position.y) {
			position.x += 1;
			position.y -= 1;
		}
		else if (goal.y > position.y) {
			position.x += 1;
			position.y += 1;
		}
		else {
			position.x += 2;
		}
	}
	else if (goal.y < position.y) {
		position.y -= 2;
	}
	else if (goal.y > position.y) {
		position.y += 2;
	}

}
